package com.cicc.beergamevirtualsimulationsystem.common.exception;

import cn.hutool.json.JSONUtil;
import com.cicc.beergamevirtualsimulationsystem.model.R;
import jakarta.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.servlet.NoHandlerFoundException;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 统一异常处理器
 * @author sunny
 */
@RestControllerAdvice
public class BaseExceptionHandler {

    private static final Logger log = LoggerFactory.getLogger(BaseExceptionHandler.class);

    // 未知错误
    public static final int UNKNOWN_CODE = 50000;

    // 资源找不到
    public static final int NOT_FOUND_CODE = 50001;

    // 请求体过大
    public static final int PAYLOAD_TOO_LARGE_CODE = 50002;

    // 参数校验错误
    public static final int PARAMETER_INVALID_CODE = 50003;

    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public R handleException(HttpServletRequest request, Exception exception) {
        log.error(exceptionMessage("Unknown exception", request), exception);
        return R.failed(UNKNOWN_CODE, exception.getMessage());
    }

    @ExceptionHandler(BizException.class)
    public R handleBizException(HttpServletRequest request, BizException exception) {
        log.warn(exceptionMessage("Biz exception", request));
        return R.failed(exception.getErrCode(), exception.getMessage());
    }

    @ExceptionHandler(NoHandlerFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public R handleNoHandlerFoundException(HttpServletRequest request, NoHandlerFoundException exception) {
        log.warn(exceptionMessage("Not found exception", request));
        return R.failed(NOT_FOUND_CODE, exception.getMessage());
    }

    @ExceptionHandler(BindException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public R handleBindException(HttpServletRequest request, BindException exception) {
        log.warn(exceptionMessage("Bind exception", request));
        return wrapperBindingResult(exception.getBindingResult());
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.OK)
    public R handleMethodArgumentNotValid(HttpServletRequest request, MethodArgumentNotValidException exception) {
        log.warn(exceptionMessage("Method arg invalid exception", request));
        /*Map<String, String> map = new HashMap<>();
        BindingResult bindingResult = exception.getBindingResult();
        bindingResult.getFieldErrors().forEach(fieldError -> {
            String message = fieldError.getDefaultMessage();
            String field = fieldError.getField();
            map.put(field, message);
        });
        return R.failed(map);*/
        BindingResult bindingResult = exception.getBindingResult();
        String errorMsg = bindingResult.getFieldErrors().stream().map(DefaultMessageSourceResolvable::getDefaultMessage).toList().get(0);
        return R.failed(errorMsg);
        //return wrapperBindingResult(exception.getBindingResult());
    }

    @ExceptionHandler(MaxUploadSizeExceededException.class)
    @ResponseStatus(HttpStatus.PAYLOAD_TOO_LARGE)
    public R handleMethodArgumentNotValid(HttpServletRequest request, MaxUploadSizeExceededException exception) {
        log.warn(exceptionMessage("Multipart exception", request));
        return R.failed(PAYLOAD_TOO_LARGE_CODE, exception.getMessage());
    }

    private String exceptionMessage(String message, HttpServletRequest request) {
        return String.format(message + ", Request: {URI=%s, method=%s}", request.getRequestURI(), request.getMethod());
    }

    private R wrapperBindingResult(BindingResult bindingResult) {
        return R.failed(PARAMETER_INVALID_CODE, bindingResult.getAllErrors().get(0).getDefaultMessage());
    }
}
